﻿using System;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using BuildServer.Models;

namespace ArivisBuildService
{
    class Utf8StringWriter : StringWriter
    {
        public override Encoding Encoding { get { return Encoding.UTF8; } }
    }

    class BuildRunInfo
    {
        /// <summary>
        /// DB id of the BuildRun
        /// </summary>
        public int BuildRunId { get; set; }

        /// <summary>
        /// Content for the .props file
        /// </summary>
        public string BuildProps { get; set; }

        /// <summary>
        /// Content for the .proj file
        /// </summary>
        public string BuildScript { get; set; }

        /// <summary>
        /// Label used to create all file and folder names for this build run
        /// </summary>
        public string Label { get; set; }

        /// <summary>
        /// Working directory for msbuild.exe
        /// </summary>
        /// <remarks>
        /// This will contain all build scripts and build folders
        /// </remarks>
        public string WorkingDirectory { get; set; }

        /// <summary>
        /// The build target(s) given to msbuild.exe
        /// </summary>
        public string Targets { get; set; }

        /// <summary>
        /// Gets the fully qualified path for the .props file
        /// </summary>
        public string BuildPropsPath { get { return GetBuildFilePath(".props"); } }

        /// <summary>
        /// Gets the fully qualified path for the .proj file
        /// </summary>
        public string BuildScriptPath { get { return GetBuildFilePath(".proj"); } }

        /// <summary>
        /// Gets the fully qualified path for the msbuild log file
        /// </summary>
        public string BuildLogPath { get { return GetBuildFilePath(".log"); } }

        /// <summary>
        /// Gets the fully qualified path for the compile log file
        /// </summary>
        public string CompileLogPath { get { return Path.Combine(WorkingDirectory, Label, "compilelog.html"); } }

        private string GetBuildFilePath(string extension)
        {
            return Path.Combine(WorkingDirectory, "build_" + Label + extension);
        }

        public static BuildRunInfo GetNext()
        {
            using (var db = new BuildServerDbContext())
            {
                // find next scheduled run
                var dbset = db.BuildRuns
                    .Include(x => x.BuildDefinition)
                    .Include(x => x.BuildDefinition.BuildTemplate)
                    .Include(x => x.BuildDefinition.BuildProperties);
                var buildRun = dbset.OrderBy(x => x.Scheduled).FirstOrDefault(x => x.Scheduled.HasValue && !x.Start.HasValue);
                if (buildRun == null)
                {
                    Console.WriteLine("{0}: No scheduled build run found.", DateTime.Now);
                    return null;
                }

                // update build run
                buildRun.DoStart();
                db.SaveChanges();

                // get build definition
                BuildDefinition buildDefinition = buildRun.BuildDefinition;

                // create build run info
                return new BuildRunInfo
                {
                    BuildRunId = buildRun.Id,
                    Targets = buildRun.Targets,
                    BuildProps = GetBuildProps(buildDefinition),
                    BuildScript = buildDefinition.BuildTemplate.BuildScript,
                    Label = buildDefinition.BuildLabel,
                    WorkingDirectory = buildDefinition.BuildTemplate.WorkingDirectory,
                };
            }
        }

        private static string GetBuildProps(BuildDefinition buildDefinition)
        {
            var xmlTemplate = buildDefinition.BuildTemplate.BuildProps;
            var buildProperties = buildDefinition.BuildProperties.ToList();

            // load xml
            XDocument document = XDocument.Parse(xmlTemplate);
            XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";

            // update properties
            foreach (var propertyGroupElement in document.Root.Elements(msbuild + "PropertyGroup"))
            {
                string group = (string)propertyGroupElement.Attribute("Label") ?? string.Empty;
                foreach (var propertyElement in propertyGroupElement.Elements())
                {
                    string name = propertyElement.Name.LocalName;
                    string value = buildDefinition.GetBuildProperty(name);
                    if (value != null)
                        propertyElement.Value = value;
                }
            }

            // save xml
            document.Declaration = new XDeclaration("1.0", "utf-8", null);
            using (var writer = new Utf8StringWriter())
            {
                document.Save(writer, SaveOptions.None);
                return writer.ToString();
            }
        }
    }

}
